`timescale 1ns/100ps
`default_nettype none

// SRAM BASE ADDRESS
`define Y_BASE_ADDR 18'd70144
`define U_BASE_ADDR 18'd108544
`define V_BASE_ADDR 18'd127744
`define RGB_BASE_ADDR 18'd146944

/***********************************************
 * Interpolation and Colour Space Conversion
 ***********************************************/
module InC (
    input logic CLOCK_50_I,             // 50 MHz Clock
    input logic resetn,                 // Reset
    input logic start,               // Start signal
    input logic [15:0] SRAM_Data,         // Data read from SRAM
    
    output logic [15:0] SRAM_Write,         // Data to write to SRAM
    output logic [17:0] SRAM_Addr,        // SRAM address
    output logic we,                    // SRAM write enable
    output logic finish              // Finish signal
);

/***********************************
 * Variable declaration
 ***********************************/

// Multiplier operates
logic [31:0]    op01, 
                op02, 
                op11, 
                op12, 
                op21, 
                op22, 
                result0, 
                result1, 
                result2;

// Main state
enum logic [4:0] {
    S_IDLE,
    S0,
    S1,
    S2,
    S3,
    S4,
    S5,
    S6,
    S7,
    S8,
    S9,
    S10,
    S11,
    S12,
    S13,
    S14,
    S15,
    S16,
    S17
} state;

// Sub state
enum logic [4:0] {
    ss0,
    ss1,
    ss2,
    ss3,
    ss4,
    ss5,
    ss6,
    ss7,
    ss8
} substate;

// SRAM_Addr offset
logic [17:0]    y_offset,
                u_offset,
                v_offset;
logic [17:0] rgb_offset;

// Buffer to hold Y values read from SRAM
logic [7:0] Y_reg [1:0];

// Buffer to hold U values used for Interpolation
logic [7:0] UR[5:0];
// Buffer to hold Y values used for Interpolation
logic [7:0] VR[5:0];

// U Interpolation
logic uIntStart;    // Start signal for U Interpolation
logic uIntFinish;   // Finish signal for U Interpolation
logic [31:0] uIntMulOp1, uIntMulOp2, uIntMulResult; // Multiplication operants for U Interpolation
logic [31:0] uIntResult;    // Result of U Interpolation

// V Interpolation
logic vIntStart;    // Start signal for V Interpolation
logic vIntFinish;   // Finish signal for V Interpolation
logic [31:0] vIntMulOp1, vIntMulOp2, vIntMulResult; // Multiplication operants for V Interpolation
logic [31:0] vIntResult;    // Result of V Interpolation

// RGB buffer for colour space conversion
logic [15:0] R_out_buf;
logic [15:0] G_out_buf;
logic [15:0] B_out_buf;

// Colour Space Convertion Even
logic cscEStart;
logic cscEFinish;
logic [31:0] cscEMulOp1, cscEMulOp2, cscEMulResult;
logic [7:0] cscER, cscEG, cscEB;        // R,G,B result of CSC_E

// Colour Space Convertion Odd
logic cscOStart;
logic cscOFinish;
logic [31:0]    cscOMulOp10, cscOMulOp20, cscOMulResult0,
                cscOMulOp11, cscOMulOp21, cscOMulResult1,
                cscOMulOp12, cscOMulOp22, cscOMulResult2;
logic [7:0] cscOR, cscOG, cscOB;        // R,G,B result of CSC_O

logic [31:0] U_out_buf, V_out_buf;
logic [15:0] U_in_buf, V_in_buf;

// Column and row counter
logic [15:0] column;     // Column count
logic [7:0] row;        // Row counter from 0 to 239

/************************************
 * Submodule declaration
 ************************************/

// Instantiate 3 multipliers
Mul32 mul0(
    .op1(op01),
    .op2(op02),
    .result(result0)
);
Mul32 mul1(
    .op1(op11),
    .op2(op12),
    .result(result1)
);
Mul32 mul2(
    .op1(op21),
    .op2(op22),
    .result(result2)
);

// U Interpolation module
Interpolation U_interpolation(
    .CLOCK_50_I(CLOCK_50_I),
    .resetn(resetn),
    
    .R(UR),
    .start(uIntStart),
    
    .mulResult(uIntMulResult),
    .mulOp1(uIntMulOp1),
    .mulOp2(uIntMulOp2),

    .intResult(uIntResult),
    .finish(uIntFinish)
);

// V Interpolation module
Interpolation V_interpolation(
    .CLOCK_50_I(CLOCK_50_I),
    .resetn(resetn),
    
    .R(VR),
    .start(vIntStart),
    
    .mulResult(vIntMulResult),
    .mulOp1(vIntMulOp1),
    .mulOp2(vIntMulOp2),

    .intResult(vIntResult),
    .finish(vIntFinish)
);

// Colour Space Conversion Even Module
Even_Colour_Space_Conv CSC_E (
    .CLOCK_50_I(CLOCK_50_I),
    .resetn(resetn),

    .Y({24'b0, Y_reg[0]}),
    .U(UR[2]),
    .V(VR[2]),
    .start(cscEStart),

    .mulResult(cscEMulResult),
    .mulOp1(cscEMulOp1),
    .mulOp2(cscEMulOp2),
    
    .R(cscER),
    .G(cscEG),
    .B(cscEB),
    .finish(cscEFinish)
);

Odd_Colour_Space_Conv CSC_O (
    .CLOCK_50_I(CLOCK_50_I),
    .resetn(resetn),
    
    .Y({24'b0, Y_reg[1]}),
    .U(U_out_buf),
    .V(V_out_buf),
    .start(cscOStart),

    .mulResult1(cscOMulResult0),
    .mul1_Op1(cscOMulOp10),
    .mul1_Op2(cscOMulOp20),
    .mulResult2(cscOMulResult1),
    .mul2_Op1(cscOMulOp11),
    .mul2_Op2(cscOMulOp21),
    .mulResult3(cscOMulResult2),
    .mul3_Op1(cscOMulOp12),
    .mul3_Op2(cscOMulOp22),
    
    .R(cscOR),
    .G(cscOG),
    .B(cscOB),
    .finish(cscOFinish)
);


/**********************************
 * Start logic
 **********************************/

always_ff @ (posedge CLOCK_50_I or negedge resetn) begin
    if (resetn == 1'b0) begin
        // Initialize offsets
        y_offset <= 18'b0;
        u_offset <= 18'b0;
        v_offset <= 18'b0;
        rgb_offset <= 18'b0;

        // Initialize start signals
        uIntStart <= 1'b0;
        vIntStart <= 1'b0;
        cscEStart <= 1'b0;
        cscOStart <= 1'b0;
        
        // Initialize registers
        Y_reg[0] <= 8'b0;
        Y_reg[1] <= 8'b0;
        UR[0] <= 8'b0;
        UR[1] <= 8'b0;
        UR[2] <= 8'b0;
        UR[3] <= 8'b0;
        UR[4] <= 8'b0;
        UR[5] <= 8'b0;
        VR[0] <= 8'b0;
        VR[1] <= 8'b0;
        VR[2] <= 8'b0;
        VR[3] <= 8'b0;
        VR[4] <= 8'b0;
        VR[5] <= 8'b0;
        R_out_buf <= 16'b0;
        G_out_buf <= 16'b0;
        B_out_buf <= 16'b0;
        U_out_buf <= 32'b0;
        V_out_buf <= 32'b0;
        U_in_buf <= 16'b0;
        V_in_buf <= 16'b0;

        // Initialize row and column count
        row <= 8'b0;
        column <= 16'b0;

        // SRAM signals
        SRAM_Addr <= 18'b0;
        SRAM_Write <= 16'b0;
        we <= 1'b0;

        // Finish
        finish <= 1'b0;
        
        // Initialize states
        state <= S_IDLE;
        substate <= ss0;
    end else begin
        case(state)
        S_IDLE: begin
            if (start) begin
                // Initialize offsets
                y_offset <= 18'b0;
                u_offset <= 18'b0;
                v_offset <= 18'b0;
                rgb_offset <= 18'b0;

                // Initialize start signals
                uIntStart <= 1'b0;
                vIntStart <= 1'b0;
                cscEStart <= 1'b0;
                cscOStart <= 1'b0;

                // Initialize registers
                Y_reg[0] <= 8'b0;
                Y_reg[1] <= 8'b0;
                UR[0] <= 8'b0;
                UR[1] <= 8'b0;
                UR[2] <= 8'b0;
                UR[3] <= 8'b0;
                UR[4] <= 8'b0;
                UR[5] <= 8'b0;
                VR[0] <= 8'b0;
                VR[1] <= 8'b0;
                VR[2] <= 8'b0;
                VR[3] <= 8'b0;
                VR[4] <= 8'b0;
                VR[5] <= 8'b0;
                R_out_buf <= 16'b0;
                G_out_buf <= 16'b0;
                B_out_buf <= 16'b0;
                U_out_buf <= 32'b0;
                V_out_buf <= 32'b0;
                U_in_buf <= 16'b0;
                V_in_buf <= 16'b0;

                // Initialize row and column count
                row <= 8'b0;
                column <= 16'b0;
         
                // SRAM signals
                SRAM_Addr <= 18'b0;
                SRAM_Write <= 16'b0;
                we <= 1'b0;

                // Finish
                finish <= 1'b0;
                      
                // Initialize states
                state <= S0;
                substate <= ss0;
            end
        end
        S0: begin
            case(substate)
            ss0: begin
                // Set we to 0, in case it was 1 during last cycle
                we <= 1'b0;
                // SRAM_Addr to Y(0,1)
                SRAM_Addr <= `Y_BASE_ADDR + y_offset;
                y_offset <= y_offset + 18'b1;
                substate <= ss1;
            end
            ss1: begin
                // SRAM_Addr to U(0,1)
                SRAM_Addr <= `U_BASE_ADDR + u_offset;
                u_offset <= u_offset + 18'b1;
                substate <= ss2;
            end
            ss2: begin
                // SRAM_Addr to V(0,1)
                SRAM_Addr <= `V_BASE_ADDR + v_offset;
                v_offset <= v_offset + 18'b1;

                substate <= ss3;
            end
            ss3: begin
                // SRAM_Addr to U(2,3)
                SRAM_Addr <= `U_BASE_ADDR + u_offset;
                u_offset <= u_offset + 18'b1;
                
                // SRAM_Data is Y(0,1), load into Y_reg
                Y_reg[0] <= SRAM_Data[15:8];
                Y_reg[1] <= SRAM_Data[7:0];
                
                substate <= ss4;
            end
            ss4: begin
                // SRAM_Addr to V(2,3)
                SRAM_Addr <= `V_BASE_ADDR + v_offset;
                v_offset <= v_offset + 18'b1;
                
                // SRAM_Data is U(0,1), load into UR[0 1 2 3]
                UR[0] <= SRAM_Data[15:8];
                UR[1] <= SRAM_Data[15:8];
                UR[2] <= SRAM_Data[15:8];
                UR[3] <= SRAM_Data[7:0];

                substate <= ss5;
            end
            ss5: begin
                // SRAM_Data is V(0,1), load into VR[0 1 2 3]
                VR[0] <= SRAM_Data[15:8];
                VR[1] <= SRAM_Data[15:8];
                VR[2] <= SRAM_Data[15:8];
                VR[3] <= SRAM_Data[7:0];
                
                substate <= ss6;
            end
            ss6: begin
                // SRAM_Data is U(2,3), load into UR[4 5]
                UR[4] <= SRAM_Data[15:8];
                UR[5] <= SRAM_Data[7:0];
                              
                substate <= ss7;
            end
            ss7: begin
                // SRAM_Data is V(2,3), load into VR[4 5]
                VR[4] <= SRAM_Data[15:8];
                VR[5] <= SRAM_Data[7:0];
                           
                // All address is stable in next cycle, start
                // Interpolation and CSC_Even
                uIntStart <= 1'b1;
                vIntStart <= 1'b1;
                cscEStart <= 1'b1;

                substate <= ss8;
            end
            ss8: begin
                // Set start signal back to 0
                uIntStart <= 1'b0;
                vIntStart <= 1'b0;
                cscEStart <= 1'b0;
                
                // Start column count
                column <= 16'b0; 
                
                // Go to s1
                state <= S1;
                // Reset substate
                substate <= ss0;
            end
            endcase
        end
        S1: begin
            case(substate)
            ss0: begin
                // SRAM_Addr to U(4,5)
                SRAM_Addr <= `U_BASE_ADDR + u_offset;
                u_offset <= u_offset + 18'b1;

                substate <= ss1;
            end
            ss1: begin
                // SRAM_Addr to V(4,5)
                SRAM_Addr <= `V_BASE_ADDR + v_offset;
                v_offset <= v_offset + 18'b1;
                
                substate <= ss2;
            end
            ss2: begin
                // SRAM_Addr to Y(2,3)
                SRAM_Addr <= `Y_BASE_ADDR + y_offset;
                y_offset <= y_offset + 18'b1;

                substate <= ss3;
            end
            ss3: begin
                // SRAM_Data is U(4,5), load into UR and U_in_buf[15:8]
                //UR <= {SRAM_Data[15:8], UR[5], UR[4], UR[3], UR[2], UR[1]};     // Shift and load U(4) into UR
                UR[5] <= SRAM_Data[15:8];
                UR[4] <= UR[5];
                UR[3] <= UR[4];
                UR[2] <= UR[3];
                UR[1] <= UR[2];
                UR[0] <= UR[1];
                U_in_buf[7:0] <= SRAM_Data[7:0];                                // Load U(5) into U_in_buf[7:0]
                
                // uIntResult and vIntResult is stable, store them into
                // U_out_buf and V_out_buf
                U_out_buf <= uIntResult;
                V_out_buf <= vIntResult;
                
                // Input of CSC_O will be stable at next cycle, start CSC_O
                cscOStart <= 1'b1;
                
                substate <= ss4;
            end
            ss4: begin
                // SRAM_Data is V(4,5), load into VR and V_in_buf[15:8]
                //VR <= {SRAM_Data[15:8], VR[5], VR[4], VR[3], VR[2], VR[1]};     // Shift and load V(4) into VR
                VR[5] <= SRAM_Data[15:8];
                VR[4] <= VR[5];
                VR[3] <= VR[4];
                VR[2] <= VR[3];
                VR[1] <= VR[2];
                VR[0] <= VR[1];
                V_in_buf[7:0] <= SRAM_Data[7:0];                                // Load V(5) into V_in_buf[7:0]

                // CSC_E RGB result is stable, store them into RGB_out_buf
                R_out_buf[15:8] <= cscER;
                G_out_buf[15:8] <= cscEG;
                B_out_buf[15:8] <= cscEB;

                // set CSS_O.start back to 0
                cscOStart <= 1'b0;
                
                // Advance column count
                column <= column + 16'b1;

                // Go to S2
                state <= S2;
                // Reset substate
                substate <= ss0;
            end
			endcase
        end
        S2: begin
            case(substate)
            ss0: begin
                // SRAM_Data is Y(2,3), load into Y_reg[0 1]
                Y_reg[0] <= SRAM_Data[15:8];
                Y_reg[1] <= SRAM_Data[7:0];
                
                // All address is stable in next cycle, start
                // Interpolation and CSC_Even
                uIntStart <= 1'b1;
                vIntStart <= 1'b1;
                cscEStart <= 1'b1;

                substate <= ss1;
            end
            ss1: begin
                // SRAM_Addr to R(0,1)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= {R_out_buf[15:8], cscOR};
                // WE to 1
                we <= 1'b1;

                // CSC_O RGB result is stable, store them into RGB_out_buf
                R_out_buf[7:0] <= cscOR;
                G_out_buf[7:0] <= cscOG;
                B_out_buf[7:0] <= cscOB;
                
                // set start signals back to 0
                uIntStart <= 1'b0;
                vIntStart <= 1'b0;
                cscEStart <= 1'b0;
                
                // Advance column count
                column <= column + 16'b1;
                
                // Go to S3
                state <= S3;
                // Reset substate
                substate <= ss0;
            end
			endcase
        end
        S3: begin
            case(substate)
            ss0: begin
                // SRAM_Addr to G(0,1)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= G_out_buf;
                // WE to 1
                we <= 1'b1;

                substate <= ss1;
            end
            ss1: begin
                // SRAM_Addr to B(0,1)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= B_out_buf;
                // WE to 1
                we <= 1'b1;

                substate <= ss2;
            end
            ss2: begin
                // SRAM_Addr to Y(4,5)
                SRAM_Addr <= `Y_BASE_ADDR + y_offset;
                y_offset <= y_offset + 18'b1;
                // WE to 0
                we <= 1'b0;

                substate <= ss3;
            end
            ss3: begin
                // SRAM_Addr to U(6,7)
                SRAM_Addr <= `U_BASE_ADDR + u_offset;
                u_offset <= u_offset + 18'b1;

                // uIntResult and vIntResult is stable, store them into
                // U_out_buf and V_out_buf
                U_out_buf <= uIntResult;
                V_out_buf <= vIntResult;

                // Input of CSC_O will be stable at next cycle, start CSC_O
                cscOStart <= 1'b1;
                
                substate <= ss4;
            end
            ss4: begin
                // SRAM_Addr to V(6,7)
                SRAM_Addr <= `V_BASE_ADDR + v_offset;
                v_offset <= v_offset + 18'b1;
                
                // CSC_E RGB result is stable, store them into RGB_out_buf
                R_out_buf[15:8] <= cscER;
                G_out_buf[15:8] <= cscEG;
                B_out_buf[15:8] <= cscEB;

                // Load U_in_buf and V_in_buf to UR and VR
                //UR = {U_in_buf[7:0], UR[5], UR[4], UR[3], UR[2], UR[1]};
                //VR = {V_in_buf[7:0], VR[5], VR[4], VR[3], VR[2], VR[1]};
                UR[5] <= U_in_buf[7:0];
                UR[4] <= UR[5];
                UR[3] <= UR[4];
                UR[2] <= UR[3];
                UR[1] <= UR[2];
                UR[0] <= UR[1];
                VR[5] <= V_in_buf[7:0];
                VR[4] <= VR[5];
                VR[3] <= VR[4];
                VR[2] <= VR[3];
                VR[1] <= VR[2];
                VR[0] <= VR[1];
                
                // set CSS_O.start back to 0
                cscOStart <= 1'b0;
                
                // Advance column count
                column <= column + 16'b1;
                
                // Go to S4
                state <= S4;
                // Reset substate
                substate <= ss0;
            end
			endcase
        end
        S4: begin
            case(substate)
            ss0: begin
                // SRAM_Data is Y(4,5), load into Y_reg[0 1]
                Y_reg[0] <= SRAM_Data[15:8];
                Y_reg[1] <= SRAM_Data[7:0];

                // All address is stable in next cycle, start
                // Interpolation and CSC_Even
                uIntStart <= 1'b1;
                vIntStart <= 1'b1;
                cscEStart <= 1'b1;

                substate <= ss1;
            end
            ss1: begin
                // SRAM_Addr to R(2,3)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= {R_out_buf[15:8], cscOR};
                // WE to 1
                we <= 1'b1;

                // SRAM_Data is U(6,7), load into U_in_buf
                U_in_buf <= SRAM_Data;
                
                // CSC_O RGB result is stable, store them into RGB_out_buf
                R_out_buf[7:0] <= cscOR;
                G_out_buf[7:0] <= cscOG;
                B_out_buf[7:0] <= cscOB;
                
                // set start signals back to 0
                uIntStart <= 1'b0;
                vIntStart <= 1'b0;
                cscEStart <= 1'b0;

                // Advance column count
                column <= column + 16'b1;
                
                // Go to S5
                state <= S5;
                // Reset substate
                substate <= ss0;
            end
			endcase
        end
        /*******************************************
         * Middle states begin
         *******************************************/
        S5: begin
            case(substate)
            ss0: begin
                // SRAM_Addr to G(n-2,n-1)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= G_out_buf;
                // WE to 1
                we <= 1'b1;

                // SRAM_Data is V(n/2+4, n/2+5), load into V_in_buf
                V_in_buf <= SRAM_Data;
                
                substate <= ss1;
            end
            ss1: begin
                // SRAM_Addr to B(n-2,n-1)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= B_out_buf;
                // WE to 1
                we <= 1'b1;

                substate <= ss2;
            end
            ss2: begin
                // SRAM_Addr to Y(n+2,n+3)
                SRAM_Addr <= `Y_BASE_ADDR + y_offset;
                y_offset <= y_offset + 18'b1;
                // WE to 0
                we <= 1'b0;

                substate <= ss3;
            end
            ss3: begin
                // uIntResult and vIntResult is stable, store them into
                // U_out_buf and V_out_buf
                U_out_buf <= uIntResult;
                V_out_buf <= vIntResult;

                // Input of CSC_O will be stable at next cycle, start CSC_O
                cscOStart <= 1'b1;
                
                substate <= ss4; 
            end
            ss4: begin
                // CSC_E RGB result is stable, store them into RGB_out_buf
                R_out_buf[15:8] <= cscER;
                G_out_buf[15:8] <= cscEG;
                B_out_buf[15:8] <= cscEB;
                
                // set CSS_O.start back to 0
                cscOStart <= 1'b0;
                
                // Advance column count
                column <= column + 16'b1;
                
                // Go to S6
                state <= S6;
                // Reset substate
                substate <= ss0;
            end
            endcase
        end
        S6: begin
            case(substate)
            ss0: begin
                // SRAM_Data is Y(n+2,n+3), load into Y_reg[0 1]
                Y_reg[0] <= SRAM_Data[15:8];
                Y_reg[1] <= SRAM_Data[7:0];
                
                // Load U_in_buf and V_in_buf to UR and VR
                //UR = {U_in_buf[15:8], UR[5], UR[4], UR[3], UR[2], UR[1]};
                //VR = {V_in_buf[15:8], VR[5], VR[4], VR[3], VR[2], VR[1]};
                UR[5] <= U_in_buf[15:8];
                UR[4] <= UR[5];
                UR[3] <= UR[4];
                UR[2] <= UR[3];
                UR[1] <= UR[2];
                UR[0] <= UR[1];
                VR[5] <= V_in_buf[15:8];
                VR[4] <= VR[5];
                VR[3] <= VR[4];
                VR[2] <= VR[3];
                VR[1] <= VR[2];
                VR[0] <= VR[1];

                // All address is stable in next cycle, start
                // Interpolation and CSC_Even
                uIntStart <= 1'b1;
                vIntStart <= 1'b1;
                cscEStart <= 1'b1;

                substate <= ss1;
            end
            ss1: begin
                // SRAM_Addr to R(n-2,n-1)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= {R_out_buf[15:8], cscOR};
                // WE to 1
                we <= 1'b1;

                // CSC_O RGB result is stable, store them into RGB_out_buf
                R_out_buf[7:0] <= cscOR;
                G_out_buf[7:0] <= cscOG;
                B_out_buf[7:0] <= cscOB;
                
                // set start signals back to 0
                uIntStart <= 1'b0;
                vIntStart <= 1'b0;
                cscEStart <= 1'b0;
                
                // Advance column count
                column <= column + 16'b1;

                // Go to S7
                state <= S7;
                // Reset substate
                substate <= ss0;
            end
            endcase
        end
        S7: begin
            case(substate)
            ss0: begin
                // SRAM_Addr to G(n-2,n-1)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= G_out_buf;
                // WE to 1
                we <= 1'b1;
                
                substate <= ss1;
            end
            ss1: begin
                // SRAM_Addr to B(n-2,n-1)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= B_out_buf;
                // WE to 1
                we <= 1'b1;

                substate <= ss2;
            end
            ss2: begin
                // SRAM_Addr to Y(n+2,n+3)
                SRAM_Addr <= `Y_BASE_ADDR + y_offset;
                y_offset <= y_offset + 18'b1;
                // WE to 0
                we <= 1'b0;

                substate <= ss3;
            end
            ss3: begin
                // SRAM_Addr to U(n/2+5, n/2+6)
                SRAM_Addr <= `U_BASE_ADDR + u_offset;
                u_offset <= u_offset + 18'b1;

                // uIntResult and vIntResult is stable, store them into
                // U_out_buf and V_out_buf
                U_out_buf <= uIntResult;
                V_out_buf <= vIntResult;

                // Input of CSC_O will be stable at next cycle, start CSC_O
                cscOStart <= 1'b1;
                
                substate <= ss4; 
            end
            ss4: begin
                // SRAM_Addr to V(n/2+5, n/2+6)
                SRAM_Addr <= `V_BASE_ADDR + v_offset;
                v_offset <= v_offset + 18'b1;

                // CSC_E RGB result is stable, store them into RGB_out_buf
                R_out_buf[15:8] <= cscER;
                G_out_buf[15:8] <= cscEG;
                B_out_buf[15:8] <= cscEB;
                
                // set CSS_O.start back to 0
                cscOStart <= 1'b0;
                
                // Advance column count
                column <= column + 16'b1;
                
                // Go to S8
                state <= S8;
                // Reset substate
                substate <= ss0;
            end
            endcase
        end
        S8: begin
            case(substate)
            ss0: begin
                // SRAM_Data is Y(n+2,n+3), load into Y_reg[0 1]
                Y_reg[0] <= SRAM_Data[15:8];
                Y_reg[1] <= SRAM_Data[7:0];

                // Load U_in_buf and V_in_buf to UR and VR
                //UR = {U_in_buf[7:0], UR[5], UR[4], UR[3], UR[2], UR[1]};
                //VR = {V_in_buf[7:0], VR[5], VR[4], VR[3], VR[2], VR[1]};
                UR[5] <= U_in_buf[7:0];
                UR[4] <= UR[5];
                UR[3] <= UR[4];
                UR[2] <= UR[3];
                UR[1] <= UR[2];
                UR[0] <= UR[1];
                VR[5] <= V_in_buf[7:0];
                VR[4] <= VR[5];
                VR[3] <= VR[4];
                VR[2] <= VR[3];
                VR[1] <= VR[2];
                VR[0] <= VR[1];

                // All address is stable in next cycle, start
                // Interpolation and CSC_Even
                uIntStart <= 1'b1;
                vIntStart <= 1'b1;
                cscEStart <= 1'b1;

                substate <= ss1;
            end
            ss1: begin
                // SRAM_Addr to R(n,n+1)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= {R_out_buf[15:8], cscOR};
                // WE to 1
                we <= 1'b1;

                // SRAM_Data is U(n/2+5, n/2+6), load into U_in_buf
                U_in_buf <= SRAM_Data;

                // CSC_O RGB result is stable, store them into RGB_out_buf
                R_out_buf[7:0] <= cscOR;
                G_out_buf[7:0] <= cscOG;
                B_out_buf[7:0] <= cscOB;
                
                // set start signals back to 0
                uIntStart <= 1'b0;
                vIntStart <= 1'b0;
                cscEStart <= 1'b0;
                
                // Advance column count
                column <= column + 16'b1;
                
                if (column == 16'd311) begin
                    // Go to S9
                    state <= S9;
                    // Reset substate
                    substate <= ss0;
                end else begin
                    // Go to S5
                    state <= S5;
                    // Reset substate
                    substate <= ss0;
                end
            end
            endcase
        end
        /*****************************************
		 * Lead out state--Start from n=312  S9
		 ****************************************/
		S9: begin
			case (substate)
			ss0: begin
                /****************************************
                 * This is a hack for
                 *  Issue 25: [M1] U,V memory read in last S7-ss4 and
                 *  S8-ss0 not required
                 *
                 *  In last S7-ss4 and S8-ss0, we do SRAM read for next U,V
                 *  value. This is not required as no more U,V on this row is
                 *  available. This will mess up the u,v offset counter by 1 pixel.
                 *
                 * Ideally, these 2 are not really S7 and S8, because they are
                 * different from
                 * traditional S7 and S8. We missed that at design time.
                 *
                 * The work around could be provided here is to decrement U,V
                 * offset counter by 1 in S9.
                 * Thus rolling back the offset by same amount.
                 * 
                 ****************************************/
                u_offset <= u_offset - 18'd1;
                v_offset <= v_offset - 18'd1;
                
                // SRAM_Addr to G(310,311)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= G_out_buf;
                // WE to 1
                we <= 1'b1;

                substate <= ss1;
            end
            ss1: begin
                // SRAM_Addr to B(310,311)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= B_out_buf;
                // WE to 1
                we <= 1'b1;

                substate <= ss2;
            end
            ss2: begin
                // SRAM_Addr to Y(314,315)
                SRAM_Addr <= `Y_BASE_ADDR + y_offset;
                y_offset <= y_offset + 18'b1;
                // WE to 0
                we <= 1'b0;

                substate <= ss3;
            end
            ss3: begin
                // uIntResult and vIntResult is stable, store them into
                // U_out_buf and V_out_buf
                U_out_buf <= uIntResult;
                V_out_buf <= vIntResult;

                // Input of CSC_O will be stable at next cycle, start CSC_O
                cscOStart <= 1'b1;
                
                substate <= ss4;
            end
            ss4: begin
                // CSC_E RGB result is stable, store them into RGB_out_buf
                R_out_buf[15:8] <= cscER;
                G_out_buf[15:8] <= cscEG;
                B_out_buf[15:8] <= cscEB;
                
                // set CSS_O.start back to 0
                cscOStart <= 1'b0;
                
                // Go to S10
                state <= S10;
                // Reset substate
                substate <= ss0;
            end
			endcase
		end
		S10: begin
			 case(substate)
			ss0: begin
                
                // SRAM_Data is Y(314,315), load into Y_reg[0 1]
                Y_reg[0] <= SRAM_Data[15:8];
                Y_reg[1] <= SRAM_Data[7:0];

				// Load U_in_buf and V_in_buf to UR and VR
				// UR[5] unchanged
                UR[4] <= UR[5];
                UR[3] <= UR[4];
                UR[2] <= UR[3];
                UR[1] <= UR[2];
                UR[0] <= UR[1];
                // VR[5] unchanged
                VR[4] <= VR[5];
                VR[3] <= VR[4];
                VR[2] <= VR[3];
                VR[1] <= VR[2];
                VR[0] <= VR[1];
                
                // All address is stable in next cycle, start
                // Interpolation and CSC_Even
                uIntStart <= 1'b1;
                vIntStart <= 1'b1;
                cscEStart <= 1'b1;

                substate <= ss1;
            end
            ss1: begin
                // SRAM_Addr to R(312,313)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= {R_out_buf[15:8], cscOR};
                // WE to 1
                we <= 1'b1;
                
                // CSC_O RGB result is stable, store them into RGB_out_buf
                R_out_buf[7:0] <= cscOR;
                G_out_buf[7:0] <= cscOG;
                B_out_buf[7:0] <= cscOB;
                
                // set start signals back to 0
                uIntStart <= 1'b0;
                vIntStart <= 1'b0;
                cscEStart <= 1'b0;
                
                // Go to S11
                state <= S11;
                // Reset substate
                substate <= ss0;
            end
			endcase
		end
		S11: begin
			case (substate)
			ss0: begin
                // SRAM_Addr to G(312,313)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= G_out_buf;
                // WE to 1
                we <= 1'b1;

                substate <= ss1;
            end
            ss1: begin
                // SRAM_Addr to B(312,313)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= B_out_buf;
                // WE to 1
                we <= 1'b1;

                substate <= ss2;
            end
            ss2: begin
                // SRAM_Addr to Y(316,317)
                SRAM_Addr <= `Y_BASE_ADDR + y_offset;
                y_offset <= y_offset + 18'b1;
                // WE to 0
                we <= 1'b0;

                substate <= ss3;
            end
            ss3: begin
                // uIntResult and vIntResult is stable, store them into
                // U_out_buf and V_out_buf
                U_out_buf <= uIntResult;
                V_out_buf <= vIntResult;

                // Input of CSC_O will be stable at next cycle, start CSC_O
                cscOStart <= 1'b1;
                
                substate <= ss4;
            end
            ss4: begin
                
                // CSC_E RGB result is stable, store them into RGB_out_buf
                R_out_buf[15:8] <= cscER;
                G_out_buf[15:8] <= cscEG;
                B_out_buf[15:8] <= cscEB;

                
                // set CSS_O.start back to 0
                cscOStart <= 1'b0;
                
                // Go to S12
                state <= S12;
                // Reset substate
                substate <= ss0;
            end
			endcase
		end
		S12: begin
			 case(substate)
			ss0: begin
                
                // SRAM_Data is Y(316,317), load into Y_reg[0 1]
                Y_reg[0] <= SRAM_Data[15:8];
                Y_reg[1] <= SRAM_Data[7:0];

				// Load U_in_buf and V_in_buf to UR and VR
                // UR[5] unchanged
                UR[4] <= UR[5];
                UR[3] <= UR[4];
                UR[2] <= UR[3];
                UR[1] <= UR[2];
                UR[0] <= UR[1];
                // VR[5] unchanged
                VR[4] <= VR[5];
                VR[3] <= VR[4];
                VR[2] <= VR[3];
                VR[1] <= VR[2];
                VR[0] <= VR[1];
                
                // All address is stable in next cycle, start
                // Interpolation and CSC_Even
                uIntStart <= 1'b1;
                vIntStart <= 1'b1;
                cscEStart <= 1'b1;

                substate <= ss1;
            end
            ss1: begin
                // SRAM_Addr to R(314,315)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= {R_out_buf[15:8], cscOR};
                // WE to 1
                we <= 1'b1;
                
                // CSC_O RGB result is stable, store them into RGB_out_buf
                R_out_buf[7:0] <= cscOR;
                G_out_buf[7:0] <= cscOG;
                B_out_buf[7:0] <= cscOB;
                
                // set start signals back to 0
                uIntStart <= 1'b0;
                vIntStart <= 1'b0;
                cscEStart <= 1'b0;
                
                // Go to S13
                state <= S13;
                // Reset substate
                substate <= ss0;
            end
			endcase
		end
		S13: begin
			 case (substate)
			ss0: begin
                // SRAM_Addr to G(314,315)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= G_out_buf;
                // WE to 1
                we <= 1'b1;

                substate <= ss1;
            end
            ss1: begin
                // SRAM_Addr to B(314,315)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= B_out_buf;
                // WE to 1
                we <= 1'b1;

                substate <= ss2;
            end
            ss2: begin
                // SRAM_Addr to Y(318,319)
                SRAM_Addr <= `Y_BASE_ADDR + y_offset;
                y_offset <= y_offset + 18'b1;
                // WE to 0
                we <= 1'b0;

                substate <= ss3;
            end
            ss3: begin
                // uIntResult and vIntResult is stable, store them into
                // U_out_buf and V_out_buf
                U_out_buf <= uIntResult;
                V_out_buf <= vIntResult;

                // Input of CSC_O will be stable at next cycle, start CSC_O
                cscOStart <= 1'b1;
                
                substate <= ss4;
            end
            ss4: begin
                // CSC_E RGB result is stable, store them into RGB_out_buf
                R_out_buf[15:8] <= cscER;
                G_out_buf[15:8] <= cscEG;
                B_out_buf[15:8] <= cscEB;

                
                // set CSS_O.start back to 0
                cscOStart <= 1'b0;
                
                // Go to S14
                state <= S14;
                // Reset substate
                substate <= ss0;
            end
			endcase
		end
		S14: begin
			case(substate)
			ss0: begin
                
                // SRAM_Data is Y(318,319), load into Y_reg[0 1]
                Y_reg[0] <= SRAM_Data[15:8];
                Y_reg[1] <= SRAM_Data[7:0];

				// Load U_in_buf and V_in_buf to UR and VR
                // UR[5] unchanged
                UR[4] <= UR[5];
                UR[3] <= UR[4];
                UR[2] <= UR[3];
                UR[1] <= UR[2];
                UR[0] <= UR[1];
                // VR[5] unchanged
                VR[4] <= VR[5];
                VR[3] <= VR[4];
                VR[2] <= VR[3];
                VR[1] <= VR[2];
                VR[0] <= VR[1];
                
                // All address is stable in next cycle, start
                // Interpolation and CSC_Even
                uIntStart <= 1'b1;
                vIntStart <= 1'b1;
                cscEStart <= 1'b1;

                substate <= ss1;
            end
            ss1: begin
                // SRAM_Addr to R(316,317)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= {R_out_buf[15:8], cscOR};
                // WE to 1
                we <= 1'b1;
                
                // CSC_O RGB result is stable, store them into RGB_out_buf
                R_out_buf[7:0] <= cscOR;
                G_out_buf[7:0] <= cscOG;
                B_out_buf[7:0] <= cscOB;
                
                // set start signals back to 0
                uIntStart <= 1'b0;
                vIntStart <= 1'b0;
                cscEStart <= 1'b0;
                
                // Go to S15
                state <= S15;
                // Reset substate
                substate <= ss0;
            end
			endcase
		end	
		S15: begin
			 case (substate)
			ss0: begin
                // SRAM_Addr to G(316,317)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= G_out_buf;
                // WE to 1
                we <= 1'b1;

                substate <= ss1;
            end
            ss1: begin
                // SRAM_Addr to B(316,317)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= B_out_buf;
                // WE to 1
                we <= 1'b1;

                substate <= ss2;
            end
            ss2: begin
                // WE to 0
                we <= 1'b0;

                substate <= ss3;
            end
            ss3: begin
                // uIntResult and vIntResult is stable, store them into
                // U_out_buf and V_out_buf
                U_out_buf <= uIntResult;
                V_out_buf <= vIntResult;

                // Input of CSC_O will be stable at next cycle, start CSC_O
                cscOStart <= 1'b1;
                
                substate <= ss4;
            end
            ss4: begin
                // CSC_E RGB result is stable, store them into RGB_out_buf
                R_out_buf[15:8] <= cscER;
                G_out_buf[15:8] <= cscEG;
                B_out_buf[15:8] <= cscEB;

                
                // set CSS_O.start back to 0
                cscOStart <= 1'b0;
                
                // Go to S16
                state <= S16;
                // Reset substate
                substate <= ss0;
            end
			endcase
		end
		S16: begin
			case(substate)
			ss0: begin
                
                // SRAM_Data is Y(318,319), load into Y_reg[0 1]
                Y_reg[0] <= SRAM_Data[15:8];
                Y_reg[1] <= SRAM_Data[7:0];

                substate <= ss1;
            end
            ss1: begin
                // SRAM_Addr to R(318,319)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= {R_out_buf[15:8], cscOR};
                // WE to 1
                we <= 1'b1;
                
                // CSC_O RGB result is stable, store them into RGB_out_buf
                R_out_buf[7:0] <= cscOR;
                G_out_buf[7:0] <= cscOG;
                B_out_buf[7:0] <= cscOB;
                
                // Go to S17
                state <= S17;
                // Reset substate
                substate <= ss0;
            end
			endcase
		end
		S17: begin
			case (substate)
			ss0: begin
                // SRAM_Addr to G(318,319)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= G_out_buf;
                // WE to 1
                we <= 1'b1;

                substate <= ss1;
            end
            ss1: begin
                // SRAM_Addr to B(318,319)
                SRAM_Addr <= `RGB_BASE_ADDR + rgb_offset;
                rgb_offset <= rgb_offset + 18'b1;
                // Provide Data for writing
                SRAM_Write <= B_out_buf;
                // WE to 1
                we <= 1'b1;

                substate <= ss2;
            end
            ss2: begin
                row <= row + 8'b1;
                if (row == 8'd239) begin
                    state <= S_IDLE;
                    substate <= ss0;
                    finish <= 1'b1;
                end else begin
                    // Go to S0
                    state <= S0;
                    substate <= ss0;
                end
			end
			endcase
		end
        endcase
    end
end

always_comb begin
    if (
        (state==S0 && substate==ss8) ||
        (state==S1 && (substate==ss0 || substate==ss1 || substate==ss2) ) ||
        (state==S2 && substate==ss1) ||
        (state==S3 && (substate==ss0 || substate==ss1 || substate==ss2) ) ||
        (state==S4 && substate==ss1) ||
        (state==S5 && (substate==ss0 || substate==ss1 || substate==ss2) ) ||
        (state==S6 && substate==ss1) ||
        (state==S7 && (substate==ss0 || substate==ss1 || substate==ss2) ) ||
        (state==S8 && substate==ss1) ||
        (state==S9 && (substate==ss0 || substate==ss1 || substate==ss2) ) ||
        (state==S10 && substate==ss1) ||
        (state==S11 && (substate==ss0 || substate==ss1 || substate==ss2) ) ||
        (state==S12 && substate==ss1) ||
        (state==S13 && (substate==ss0 || substate==ss1 || substate==ss2) ) ||
        (state==S14 && substate==ss1) ||
        (state==S15 && (substate==ss0 || substate==ss1 || substate==ss2) )
    ) begin
        // Setup un-used variables
        cscOMulResult0 = 32'b0;
        cscOMulResult1 = 32'b0;
        cscOMulResult2 = 32'b0;
        // Hook up operants and result of mul0 to U_interpolation
        op01 = uIntMulOp1;
        op02 = uIntMulOp2;
        uIntMulResult = result0;
        // Hook up operants and result of mul1 to V_interpolation
        op11 = vIntMulOp1;
        op12 = vIntMulOp2;
        vIntMulResult = result1;
        // Hook up operants and result of mul2 to CSC_E
        op21 = cscEMulOp1;
        op22 = cscEMulOp2;
        cscEMulResult = result2;
    end else if ( (substate==ss3) &&
        (   state==S1 ||
            state==S3 ||
            state==S5 ||
            state==S7 ||
            state==S9 ||
            state==S11 ||
            state==S13 ||
            state==S15
        )
    ) begin
        // Setup un-used variables
        op01 = 32'b0;   op02 = 32'b0;
        op11 = 32'b0;   op12 = 32'b0;
        uIntMulResult = 32'b0;
        vIntMulResult = 32'b0;
        cscOMulResult0 = 32'b0;
        cscOMulResult1 = 32'b0;
        cscOMulResult2 = 32'b0;
        // Hook up operants and result of mul2 to CSC_E
        op21 = cscEMulOp1;
        op22 = cscEMulOp2;
        cscEMulResult = result2;
    end else if (
        (state==S1 && substate==ss4) ||
        (state==S2 && substate==ss0) ||
        (state==S3 && substate==ss4) ||
        (state==S4 && substate==ss0) ||
        (state==S5 && substate==ss4) ||
        (state==S6 && substate==ss0) ||
        (state==S7 && substate==ss4) ||
        (state==S8 && substate==ss0) ||
        (state==S9 && substate==ss4) ||
        (state==S10 && substate==ss0) ||
        (state==S11 && substate==ss4) ||
        (state==S12 && substate==ss0) ||
        (state==S13 && substate==ss4) ||
        (state==S14 && substate==ss0) ||
        (state==S15 && substate==ss4) ||
        (state==S16 && substate==ss0) 
    ) begin
        // Setup un-used variables
        uIntMulResult = 32'b0;
        vIntMulResult = 32'b0;
        cscEMulResult = 32'b0;
        // Hook up operants and result of mul0,1,2 to CSC_O
        op01 = cscOMulOp10;
        op02 = cscOMulOp20;
        cscOMulResult0 = result0;
        op11 = cscOMulOp11;
        op12 = cscOMulOp21;
        cscOMulResult1 = result1;
        op21 = cscOMulOp12;
        op22 = cscOMulOp22;
        cscOMulResult2 = result2;
    end else begin
        // Provide default value for variables used.
        op01 = 32'b0;   op02 = 32'b0;
        op11 = 32'b0;   op12 = 32'b0;
        op21 = 32'b0;   op22 = 32'b0;
        uIntMulResult = 32'b0;
        vIntMulResult = 32'b0;
        cscEMulResult = 32'b0;
        cscOMulResult0 = 32'b0;
        cscOMulResult1 = 32'b0;
        cscOMulResult2 = 32'b0;
    end
end


endmodule
